
package ploy.compiler;

import java.lang.*;
import java.util.*;

import ploy.lang.*;

/** 
 * A SyntacticEnvironment encapsulates the mapping from keywords to parse
 * functions, and also what constitutes the dynamic environment for macros.
 */
public abstract class SyntacticEnvironment {

    public static SyntacticEnvironment newReplEnvironment( ) {
	SyntacticEnvironment fundamentals = new PloyFundamentals( );
	MutableSyntacticEnvironment mutable = 
	    new MutableSyntacticEnvironment( fundamentals );
	SyntacticEnvironment define =
	    new DefineSyntacticEnvironment( mutable );	
	SyntacticEnvironment defineSyntax =
	    new DefineSyntaxSyntacticEnvironment( define, mutable, mutable );
	return defineSyntax;
    }

    public abstract ParseClass keywordParser( Symbol keyword );
   
}

class PloyFundamentals extends SyntacticEnvironment {
    
    public static final Symbol appSymbol = new Symbol( "%app" );
    public static final Symbol lambdaSymbol = new Symbol( "lambda" );
    public static final Symbol constSymbol = new Symbol( "%const" );
    public static final Symbol varSymbol = new Symbol( "%var" );

    public static final ParseClass appParser = new AppParser();
    public static final ParseClass lambdaParser = new LambdaParser();
    public static final ParseClass constParser = new ConstantParser();
    public static final ParseClass varParser = new VarParser();

    public PloyFundamentals( ) {
    }

    public ParseClass keywordParser( Symbol keyword ) {
	if ( appSymbol.equals( keyword ) ) {
	    return appParser;
	} else if ( lambdaSymbol.equals( keyword ) ) {
	    return lambdaParser;
	} else if ( constSymbol.equals( keyword ) ) {
	    return constParser;
	} else if ( varSymbol.equals( keyword ) ) {
	    return varParser;
	}
	return null;
    }
}
	
class MutableSyntacticEnvironment extends SyntacticEnvironment {
    
    private Map<Symbol, ParseClass> syntacticMap;
    private SyntacticEnvironment superEnvironment;
    
    public MutableSyntacticEnvironment( ) {
	syntacticMap = new Hashtable<Symbol,ParseClass>();
	superEnvironment = null;
    }
    
    public MutableSyntacticEnvironment( SyntacticEnvironment s ) {
	syntacticMap = new Hashtable<Symbol,ParseClass>();
	superEnvironment = s;
    }
    
    public ParseClass keywordParser( Symbol keyword ) {
	ParseClass toReturn = syntacticMap.get( keyword );
	if ( ( toReturn == null ) && ( superEnvironment != null ) ) {
	    return superEnvironment.keywordParser( keyword );
	} else {
	    return toReturn;
	}
    }

    public void addParser( Symbol keyword, ParseClass parser ) {
	syntacticMap.put( keyword, parser );
    }
}

class DefineSyntacticEnvironment extends SyntacticEnvironment {
    
    public static final Symbol defineSymbol = new Symbol( "define" );

    public ParseClass defineParser;

    private SyntacticEnvironment superEnvironment;

    public DefineSyntacticEnvironment( SyntacticEnvironment s ) {
	superEnvironment = s;
	defineParser = new DefineParser( s );
    }
    
    public DefineSyntacticEnvironment( 
    	SyntacticEnvironment s,
	SyntacticEnvironment nonDefinitionalEnv 
    ) {
	superEnvironment = s;
	defineParser = new DefineParser( nonDefinitionalEnv );
    }
    
    public ParseClass keywordParser( Symbol keyword ) {
	if ( defineSymbol.equals( keyword ) ) {
	    return defineParser;
	} else if ( superEnvironment != null ) {
	    return superEnvironment.keywordParser( keyword );
	} else {
	    return null;
	}
    }
}

class DefineSyntaxSyntacticEnvironment extends SyntacticEnvironment {
    
    public static final Symbol defineSyntaxSymbol = 
	new Symbol( "defineSyntax" );

    public ParseClass defineSyntaxParser;

    private SyntacticEnvironment superEnvironment;

    public DefineSyntaxSyntacticEnvironment( MutableSyntacticEnvironment s ) {
	superEnvironment = s;
	defineSyntaxParser = new DefineSyntaxParser( s, s );
    }
    
    public DefineSyntaxSyntacticEnvironment( 
    	MutableSyntacticEnvironment s,
	SyntacticEnvironment nonDefinitionalEnv 
    ) {
	superEnvironment = s;
	defineSyntaxParser = new DefineSyntaxParser( nonDefinitionalEnv, s );
    }
    
    public DefineSyntaxSyntacticEnvironment( 
    	SyntacticEnvironment s,
	SyntacticEnvironment nonDefinitionalEnv,
	MutableSyntacticEnvironment me
    ) {
	superEnvironment = s;
	defineSyntaxParser = new DefineSyntaxParser( nonDefinitionalEnv, me );
    }
    
    public ParseClass keywordParser( Symbol keyword ) {
	if ( defineSyntaxSymbol.equals( keyword ) ) {
	    return defineSyntaxParser;
	} else if ( superEnvironment != null ) {
	    return superEnvironment.keywordParser( keyword );
	} else {
	    return null;
	}
    }
}

